#pragma once
#include "../Utils/PlatTypeUtil.h"
#include <intrin.h>

/** Thread safe counter */
class ThreadCounter
{
public:

	typedef int32 IntegerType;

	/**
	 * Default constructor.
	 *
	 * Initializes the counter to 0.
	 */
	ThreadCounter()
	{
		mCounter = 0;
	}

	/**
	 * Copy Constructor.
	 *
	 * If the counter in the Other parameter is changing from other threads, there are no
	 * guarantees as to which values you will get up to the caller to not care, synchronize
	 * or other way to make those guarantees.
	 *
	 * @param Other The other thread safe counter to copy
	 */
	ThreadCounter(const ThreadCounter& other)
	{
		mCounter = other.GetValue();
	}

	/**
	 * Constructor, initializing counter to passed in value.
	 *
	 * @param Value	Value to initialize counter to
	 */
	ThreadCounter(int32 value)
	{
		mCounter = value;
	}

	/**
	 * Increment and return new value.
	 *
	 * @return the new, incremented value
	 * @see Add, Decrement, Reset, Set, Subtract
	 */
	int32 Increment()
	{
		return (int32)_InterlockedIncrement((long*)&mCounter);
	}

	/**
	 * Adds an amount and returns the old value.
	 *
	 * @param Amount Amount to increase the counter by
	 * @return the old value
	 * @see Decrement, Increment, Reset, Set, Subtract
	 */
	int32 Add(int32 amount)
	{
		return (int32)::_InterlockedExchangeAdd((long*)&mCounter, (long)amount);
	}

	/**
	 * Decrement and return new value.
	 *
	 * @return the new, decremented value
	 * @see Add, Increment, Reset, Set, Subtract
	 */
	int32 Decrement()
	{
		return (int32)::_InterlockedDecrement((long*)&mCounter);
	}

	/**
	 * Subtracts an amount and returns the old value.
	 *
	 * @param Amount Amount to decrease the counter by
	 * @return the old value
	 * @see Add, Decrement, Increment, Reset, Set
	 */
	int32 Subtract(int32 amount)
	{
		return (int32)::_InterlockedExchangeAdd((long*)&mCounter, (long)-amount);
	}

	/**
	 * Sets the counter to a specific value and returns the old value.
	 *
	 * @param Value	Value to set the counter to
	 * @return The old value
	 * @see Add, Decrement, Increment, Reset, Subtract
	 */
	int32 Set(int32 value)
	{
		return (int32)::_InterlockedExchange((long*)&mCounter, (long)value);
	}

	/**
	 * Resets the counter's value to zero.
	 *
	 * @return the old value.
	 * @see Add, Decrement, Increment, Set, Subtract
	 */
	int32 Reset()
	{
		return (int32)::_InterlockedExchange((long*)&mCounter, 0);
	}

	/**
	 * Gets the current value.
	 *
	 * @return the current value
	 */
	int32 GetValue() const
	{
		return (int32)::_InterlockedCompareExchange((long*)&const_cast<ThreadCounter*>(this)->mCounter, 0, 0);
	}

private:

	/** Hidden on purpose as usage wouldn't be thread safe. */
	void operator=(const ThreadCounter& Other) {}

	/** Thread-safe counter */
	volatile int32 mCounter;
};








/** Frame safe counter */
class FrameCounter
{
public:

	typedef int64 IntegerType;

	/**
	 * Default constructor.
	 *
	 * Initializes the counter to 0.
	 */
	FrameCounter()
	{
		mCounter = _I64_MIN + 2;
	}

	/**
	 * Copy Constructor.
	 *
	 * If the counter in the Other parameter is changing from other threads, there are no
	 * guarantees as to which values you will get up to the caller to not care, synchronize
	 * or other way to make those guarantees.
	 *
	 * @param Other The other thread safe counter to copy
	 */
	FrameCounter(const FrameCounter& other)
	{
		mCounter = other.GetValue();
	}

	/**
	 * Constructor, initializing counter to passed in value.
	 *
	 * @param Value	Value to initialize counter to
	 */
	FrameCounter(int64 value)
	{
		mCounter = value;
	}

	/**
	 * Increment and return new value.
	 *
	 * @return the new, incremented value
	 * @see Add, Decrement, Reset, Set, Subtract
	 */
	int64 Increment()
	{
		return (int64)_InterlockedIncrement64((int64*)&mCounter);
	}

	/**
	 * Adds an amount and returns the old value.
	 *
	 * @param Amount Amount to increase the counter by
	 * @return the old value
	 * @see Decrement, Increment, Reset, Set, Subtract
	 */
	int64 Add(int64 amount)
	{
		return (int64)::_interlockedexchangeadd64((int64*)&mCounter, (int64)amount);
	}

	/**
	 * Decrement and return new value.
	 *
	 * @return the new, decremented value
	 * @see Add, Increment, Reset, Set, Subtract
	 */
	int64 Decrement()
	{
		return (int64)::_InterlockedDecrement64((int64*)&mCounter);
	}

	/**
	 * Subtracts an amount and returns the old value.
	 *
	 * @param Amount Amount to decrease the counter by
	 * @return the old value
	 * @see Add, Decrement, Increment, Reset, Set
	 */
	int64 Subtract(int64 amount)
	{
		return (int64)::_InterlockedExchangeAdd64((int64*)&mCounter, (int64)-amount);
	}

	/**
	 * Sets the counter to a specific value and returns the old value.
	 *
	 * @param Value	Value to set the counter to
	 * @return The old value
	 * @see Add, Decrement, Increment, Reset, Subtract
	 */
	int64 Set(int64 value)
	{
		return (int64)::_InterlockedExchange64((int64*)&mCounter, (int64)value);
	}

	/**
	 * Resets the counter's value to zero.
	 *
	 * @return the old value.
	 * @see Add, Decrement, Increment, Set, Subtract
	 */
	int64 Reset()
	{
		return (int64)::_InterlockedExchange64((int64*)&mCounter, 0);
	}

	/**
	 * Gets the current value.
	 *
	 * @return the current value
	 */
	int64 GetValue() const
	{
		return (int64)::_InterlockedCompareExchange64((int64*)&const_cast<FrameCounter*>(this)->mCounter, 0, 0);
	}

private:

	/** Hidden on purpose as usage wouldn't be thread safe. */
	void operator=(const FrameCounter& Other) {}

	/** Thread-safe counter */
	volatile int64 mCounter;
};
